﻿using netDxf;
using netDxf.Blocks;
using netDxf.Entities;
using netDxf.IO;
using netDxf.Objects;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Merge
{
    public class RealDxf : IDxf
    {
        private IMergeSettings settings;
        public RealDxf(IMergeSettings settings)
        {
            this.settings = settings;
        }

        public void Add3Lines(DxfDocument doc,
    double ax1, double ay1, double ax2, double ay2,
    double bx1, double by1, double bx2, double by2,
    double cx1, double cy1, double cx2, double cy2,
    bool appendAnotherVerticalLine
    )
        {
            doc.Entities.Add(new Line(new Vector2(ax1, ay1), new Vector2(ax2, ay2)));
            doc.Entities.Add(new Line(new Vector2(bx1, by1), new Vector2(bx2, by2)));
            doc.Entities.Add(new Line(new Vector2(cx1, cy1), new Vector2(cx2, cy2)));
            if (appendAnotherVerticalLine)
            {
                doc.Entities.Add(new Line(new Vector2(cx1 + 80, cy1), new Vector2(cx2 + 80, cy2)));
            }
        }


        public void AddFolderIndexLine(DxfDocument doc, int fullDir2ToIndex,
    double gridLeftX, double gridButtomY, double gridWidth)
        {
            if (!settings.FolderNeedAppendLine)
            {
                return;
            }
            double lineLeftX = gridLeftX + gridWidth / 2 - 400;
            double lineY = gridButtomY - 200;
            doc.Entities.Add(new Line(new Vector2(lineLeftX, lineY), new Vector2(lineLeftX + 1000 + fullDir2ToIndex, lineY)));
        }

        public void AddText(DxfDocument doc, string s, double x, double y, double height)
        {
            MText mtext = new MText(s.Replace("\\", "\\\\"), new Vector2(x, y), height);
            doc.Entities.Add(mtext);
        }
        public void CalcCellRect(Cell cell, IEnumerable<EntityObject> entities, Action<string, bool> updateMsg)
        {
            List<double> xs = new List<double>();
            List<double> ys = new List<double>();
            foreach (EntityObject entity in entities)
            {
                Console.WriteLine(entity.Type);
                switch (entity.Type)
                {
                    case EntityType.Line:
                        Line line = entity as Line;
                        xs.Add(line.StartPoint.X);
                        ys.Add(line.StartPoint.Y);
                        xs.Add(line.EndPoint.X);
                        ys.Add(line.EndPoint.Y);
                        break;
                    case EntityType.Polyline2D:
                        Polyline2D pline = entity as Polyline2D;
                        foreach (Polyline2DVertex v2 in pline.Vertexes)
                        {
                            xs.Add(v2.Position.X);
                            ys.Add(v2.Position.Y);
                        }
                        break;
                    case EntityType.Circle:
                        Circle circle = entity as Circle;
                        double r = circle.Radius;
                        xs.Add(circle.Center.X - r);
                        xs.Add(circle.Center.X + r);
                        ys.Add(circle.Center.Y - r);
                        ys.Add(circle.Center.Y + r);
                        break;
                    //case EntityType.MText:
                    //    MText mText = entity as MText;
                    //    xs.Add(mText.Normal.X);
                    //    ys.Add(mText.Normal.Y);
                    //    xs.Add(mText.Position.X);
                    //    ys.Add(mText.Position.Y);
                    //    break;
                    case EntityType.Dimension:
                        //Dimension dimension = entity as Dimension;
                        //xs.Add(dimension.Normal.X);
                        //ys.Add(dimension.Normal.Y);
                        //xs.Add(dimension.TextReferencePoint.X);
                        //ys.Add(dimension.TextReferencePoint.Y);
                        //xs.Add(dimension.TextReferencePoint.X);
                        //ys.Add(dimension.TextReferencePoint.Y);
                        //foreach (var en in dimension.Block.Entities)
                        //{
                        //    xs.Add(en.Normal.X);
                        //    ys.Add(en.Normal.Y);
                        //}
                        break;
                    case EntityType.Spline:
                        Spline spline = entity as Spline;
                        foreach (Vector3 v3 in spline.FitPoints)
                        {
                            xs.Add(v3.X);
                            ys.Add(v3.Y);
                        }
                        foreach (Vector3 vector3 in spline.ControlPoints)
                        {
                            xs.Add(vector3.X);
                            ys.Add(vector3.Y);
                        }
                        break;
                }
            }
            bool xAny = xs.Any();
            bool yAny = ys.Any();
            if (!xAny || !yAny)
            {
                updateMsg($"无法判断图形位置尺寸{cell.DxfFileName}", true);
            }
            double left = xAny ? xs.Min() : 50;
            double right = xAny ? xs.Max() : 3000;
            double top = yAny ? ys.Max() : 3000;
            double buttom = yAny ? ys.Min() : 50;
            Console.WriteLine($"Left={left} Buttom={buttom} top={top} righ={right}");
            cell.Left = left;
            cell.Buttom = buttom;
            cell.top = top;
            cell.right = right;
            cell.width = right - left;
            cell.height = top - buttom;
        }
        string Hash(string input)
        {
            //string s = Math.Abs(input.GetHashCode()).ToString();
            return String.Format("{0:X}", input.GetHashCode());
        }
        public void CloneDxf(Cell cell, Action<string, bool> updateMsg)
        {
            string dxfFile = cell.DxfFullName;
            updateMsg($"准备读取{dxfFile}", true);
            string hash = Hash(dxfFile);
            DxfDocument srcDoc = this.ReplaceInsertAndRead(dxfFile, hash, updateMsg);
            if (srcDoc == null)
            {
                cell.CanRead = false;
                return;
            }
            cell.CanRead = true;
            //DxfDocument srcDoc = DxfDocument.Load(dxfFile);
            Block nestedBlock = new Block($"IMPORT{hash}");

            List<EntityObject> entitiesToCalc = new List<EntityObject>();
            try
            {
                foreach (Layout layout in srcDoc.Layouts)
                {
                    foreach (EntityObject entity in srcDoc.Layouts.GetReferences(layout)
                        .Where(dr => dr.Reference is Block)
                        .SelectMany(dr => (dr.Reference as Block).Entities))
                    {
                        if (entity.Type == EntityType.Insert)
                        {
                            Insert insert = entity as Insert;
                            entitiesToCalc.AddRange(insert.Explode());
                        }
                        else
                        {
                            entitiesToCalc.Add(entity);
                        }
                        nestedBlock.Entities.Add(entity.Clone() as EntityObject);
                    }
                }
            }
            catch (Exception ex)
            {
                updateMsg(ex.Message, true);
                cell.CanRead = false;
                return;
            }
            cell.ImportedBlock = nestedBlock;
            CalcCellRect(cell, entitiesToCalc, updateMsg);
        }

        double CalcDesignWidth(double width, double gridWidht)
        {
            return Math.Ceiling(width / gridWidht) * gridWidht;
        }
        const double TEXT_MARGIN_RATE = 1.8;
        private bool NeedAnotherVerticalLine(List<Cell> rowItems, int currentIndex)
        {
            if (currentIndex >= rowItems.Count - 1)
            {
                return false;
            }
            return rowItems[currentIndex].Dir2 != rowItems[currentIndex + 1].Dir2;
        }
        private void AppendBeforeMergeFile(string file, Action<string, bool> updateMsg)
        {
            updateMsg($"准备合并{file}", true);
        }
        private void AppendAfterMergeFile(string file, Action<string, bool> updateMsg)
        {
            updateMsg($"完成合并{file}", true);
        }
        public void MergeSizeType(double gridWidth,
            double gridHeight,
            IEnumerable<Cell> cells,
            string outputDxfFile,
            LinkLabel lbl,
            IMergeSettings settings,
            Action<string, bool> updateMsg,
            Action<LinkLabel, bool> UpdateLableColor,

            Filters filters,
            Dictionary<Filter, int> dicNameOutOrder,
            Dictionary<string, string> fullDir2ToConterDict,
            Dictionary<string, int> fullDir2ToIndexDict)
        {
            bool hasCells = cells.Any();
            UpdateLableColor(lbl, hasCells);
            if (!hasCells)
                return;

            DxfDocument doc = new DxfDocument(netDxf.Header.DxfVersion.AutoCad2013);

            if (settings.IsFilterMode)
            {
                var matchingCells = cells.Where(c => Utils.WithInFilter(filters, c, settings));
                double totalHeight = gridHeight * dicNameOutOrder.Count();
                double y = totalHeight / 2;
                int dir2Counter = 0;
                foreach (var kv in dicNameOutOrder.OrderBy(x => x.Value))
                {
                    Filter filter = kv.Key;
                    string filterName = filter.Name;
                    var rowItems = matchingCells.Where(c => c.FilterName == filterName).ToList();
                    double x = 0;
                    double top = y + gridHeight;
                    if (rowItems.Any())
                    {
                        AddText(doc, $"{filter.OutputOrder} {filterName}", x + settings.TextFolderLeftX, y + gridHeight / 2, settings.TextFolderHeight);
                        Add3Lines(doc,
                        x + settings.TextFolderLeftX, y, x, y,
                            x + settings.TextFolderLeftX, top, x, top,
                            x, y, x, top, false
                            );
                    }

                    for (int currentIndex = 0; currentIndex < rowItems.Count; currentIndex++)
                    {
                        Cell cell = rowItems[currentIndex];
                        double deltaX = CalcDesignWidth(cell.width, gridWidth);
                        double right = x + deltaX;
                        double imageTextHeight = settings.TextImageHeight;
                        double imageTextHeightWithSpace = imageTextHeight * TEXT_MARGIN_RATE;
                        try
                        {
                            AppendBeforeMergeFile(cell.DxfFullName, updateMsg);
                            doc.Entities.Add(new Insert(cell.ImportedBlock, new Vector2(
                                x - cell.Left + (deltaX - cell.width) / 2,
                                y - cell.Buttom + (gridHeight - cell.height) / 2
                                )));
                            AppendAfterMergeFile(cell.DxfFullName, updateMsg);
                        }
                        catch (Exception ex)
                        {
                            updateMsg(ex.Message, true);
                        }

                        double textY = y + imageTextHeightWithSpace;

                        string folderIndexString3 = fullDir2ToConterDict[cell.FullDir];
                        string counterString = "";
                        if (settings.NeedShowIndex)
                        {
                            counterString = "【" + folderIndexString3 + "】";
                        }

                        if (settings.TextImageRows > 2)
                        {
                            AddText(doc, Path.GetFileName(cell.Dir1), x + settings.TextImageRightX, textY, imageTextHeight);
                            textY += imageTextHeightWithSpace;
                        }
                        if (settings.TextImageRows > 1)
                        {
                            dir2Counter++;
                            string dir2Text = (settings.NeedShowIndex ? counterString + "、" : "") + Path.GetFileName(cell.Dir2);
                            AddText(doc, dir2Text, x + settings.TextImageRightX, textY, imageTextHeight);
                            textY += imageTextHeightWithSpace;
                        }

                        string dxfFileText = (settings.NeedShowIndex ? counterString + " " : "") + cell.DxfFileName;
                        AddText(doc, dxfFileText, x + settings.TextImageRightX, textY, imageTextHeight);
                        AddFolderIndexLine(doc, fullDir2ToIndexDict[cell.FullDir], x, y, gridWidth);

                        if (settings.ExtractCopyCount)
                        {
                            string copyCountText = Utils.ExtractCopyCountsToIntStr(cell.DxfFileName, settings.ShowQuestionMarkOnFailure);
                            if (!string.IsNullOrEmpty(copyCountText))
                            {
                                AddText(doc, copyCountText, x + gridWidth / 2, textY +
                                    imageTextHeightWithSpace * 2, imageTextHeight * 3);
                            }
                        }

                        Add3Lines(doc,
                        x, top, right, top,
                        x, y, right, y,
                        right, y, right, y + gridHeight,
                        NeedAnotherVerticalLine(rowItems, currentIndex)
                        );


                        x += deltaX;


                    }
                    if (settings.IsFilterMode && settings.GridSpaceLine)
                    {
                        y -= gridHeight * 2;
                    }
                    else
                    {
                        y -= gridHeight;
                    }
                }
                var exludedCells = cells.Where(c => !Utils.WithInFilter(filters, c, settings));
                if (exludedCells.Any())
                {
                    double x2 = 0;
                    double top2 = y + gridHeight;
                    AddText(doc, "其他分类", x2 + settings.TextFolderLeftX, y + gridHeight / 2, settings.TextFolderHeight);

                    Add3Lines(doc,
                    x2 + settings.TextFolderLeftX, y, x2, y,
                    x2 + settings.TextFolderLeftX, top2, x2, top2,
                    x2, y, x2, top2,
                    false
                    );
                    var exludedCellsList = exludedCells.ToList();
                    for (int currentIndex = 0; currentIndex < exludedCellsList.Count; currentIndex++)
                    {
                        Cell cell = exludedCellsList[currentIndex];

                        double deltaX = CalcDesignWidth(cell.width, gridWidth);
                        double right = x2 + deltaX;
                        double imageTextHeight = settings.TextImageHeight;
                        double imageTextHeightWithSpace = imageTextHeight * TEXT_MARGIN_RATE;

                        try
                        {
                            AppendBeforeMergeFile(cell.DxfFullName, updateMsg);
                            doc.Entities.Add(new Insert(cell.ImportedBlock, new Vector2(
                                x2 - cell.Left + (deltaX - cell.width) / 2,
                                y - cell.Buttom + (gridHeight - cell.height) / 2
                                )));
                            AppendAfterMergeFile(cell.DxfFullName, updateMsg);
                        }
                        catch (Exception ex)
                        {
                            updateMsg(ex.Message, true);
                        }
                        double textY = y + imageTextHeightWithSpace;
                        if (settings.TextImageRows > 2)
                        {
                            AddText(doc, Path.GetFileName(cell.Dir1), x2 + settings.TextImageRightX, textY, imageTextHeight);
                            textY += imageTextHeightWithSpace;
                        }
                        string folderIndexString3 = fullDir2ToConterDict[cell.FullDir];
                        string counterString = "";
                        if (settings.NeedShowIndex)
                        {
                            counterString = "【" + folderIndexString3 + "】";
                        }

                        if (settings.TextImageRows > 1)
                        {
                            string dir2Text = (settings.NeedShowIndex ? counterString + "、" : "") + Path.GetFileName(cell.Dir2);
                            AddText(doc, dir2Text, x2 + settings.TextImageRightX, textY, imageTextHeight);
                            textY += imageTextHeightWithSpace;
                        }

                        string dxfFileText = (settings.NeedShowIndex ? counterString + " " : "") + cell.DxfFileName;
                        AddText(doc, dxfFileText, x2 + settings.TextImageRightX, textY, imageTextHeight);
                        AddFolderIndexLine(doc, fullDir2ToIndexDict[cell.FullDir], x2, y, gridWidth);

                        if (settings.ExtractCopyCount)
                        {
                            string copyCountText = Utils.ExtractCopyCountsToIntStr(cell.DxfFileName, settings.ShowQuestionMarkOnFailure);
                            if (!string.IsNullOrEmpty(copyCountText))
                            {
                                AddText(doc, copyCountText, x2 + gridWidth / 2, textY +
                                    imageTextHeightWithSpace * 2, imageTextHeight * 3);
                            }
                        }

                        AppendAfterMergeFile(cell.DxfFullName, updateMsg);

                        Add3Lines(doc,
   x2, top2, right, top2,
   x2, y, right, y,
   right, y, right, y + gridHeight,
                       NeedAnotherVerticalLine(exludedCellsList, currentIndex)

   );

                        x2 += deltaX;


                    }
                    //non-grouped
                }


            }
            //not filter
            else
            {
                var groups = cells.GroupBy(x => new { x.Dir1, x.Dir2 });

                double totalHeight = gridHeight * groups.Count();
                double y = totalHeight / 2;
                foreach (var kv in groups)
                {
                    double x = 0;
                    double top = y + gridHeight;

                    string d1 = Path.GetFileName(kv.Key.Dir1);
                    string d2 = Path.GetFileName(kv.Key.Dir2);

                    AddText(doc, d1, x + settings.TextFolderLeftX, y + gridHeight / 3, settings.TextFolderHeight);
                    AddText(doc, d2, x + settings.TextFolderLeftX, y + gridHeight * 2 / 3, settings.TextFolderHeight);
                    Add3Lines(doc,
                   x + settings.TextFolderLeftX, y, x, y,
   x + settings.TextFolderLeftX, top, x, top,
   x, y, x, top,
   false
   );
                    var kvCellsList = kv.ToList();
                    for (int currentIndex = 0; currentIndex < kvCellsList.Count; currentIndex++)
                    {
                        Cell cell = kvCellsList[currentIndex];
                        double deltaX = CalcDesignWidth(cell.width, gridWidth);
                        double right = x + deltaX;
                        double imageTextHeight = settings.TextImageHeight;
                        double imageTextHeightWithSpace = imageTextHeight * TEXT_MARGIN_RATE;

                        try
                        {
                            AppendBeforeMergeFile(cell.DxfFullName, updateMsg);
                            doc.Entities.Add(new Insert(cell.ImportedBlock, new Vector2(
                                x - cell.Left + (deltaX - cell.width) / 2,
                                y - cell.Buttom + (gridHeight - cell.height) / 2
                                )));
                            AppendAfterMergeFile(cell.DxfFullName, updateMsg);
                        }
                        catch (Exception ex)
                        {
                            updateMsg(ex.Message, true);
                        }
                        double textY = y + imageTextHeightWithSpace;
                        if (settings.TextImageRows > 2)
                        {
                            AddText(doc, d1, x + settings.TextImageRightX, textY, imageTextHeight);
                            textY += imageTextHeightWithSpace;
                        }
                        if (settings.TextImageRows > 1)
                        {
                            AddText(doc, d2, x + settings.TextImageRightX, textY, imageTextHeight);
                            textY += imageTextHeightWithSpace;
                        }

                        AddText(doc, cell.DxfFileName, x + settings.TextImageRightX, textY, imageTextHeight);

                        Add3Lines(doc,
    x, top, right, top,
    x, y, right, y,
    right, y, right, y + gridHeight,
                        NeedAnotherVerticalLine(kvCellsList, currentIndex)

    );
                        x += deltaX;
                    }
                    y -= gridHeight;
                }
            }

            doc.Save(outputDxfFile);
            updateMsg($"合并到{outputDxfFile}", true);


        }

        public DxfDocument ReplaceInsertAndRead(string dxfFile, string hash, Action<string, bool> updateMsg)
        {
            DxfDocument doc = null;
            try
            {
                doc = DxfDocument.Load(dxfFile);
                //RemoveSinglePolyline2D(doc);
            }
            catch (DxfVersionNotSupportedException ex)
            {
                updateMsg($"无法读入文件{dxfFile}，{ex.Message}", true);
            }
            catch (ArgumentOutOfRangeException ex)
            {
                updateMsg($"无法读入文件{dxfFile}，{ex.Message}", true);
            }
            catch (KeyNotFoundException ex)
            {
                updateMsg($"无法读入文件{dxfFile}，{ex.Message}", true);
            }
            catch (NullReferenceException ex)
            {
                updateMsg($"无法读入文件{dxfFile}，{ex.Message}", true);
            }
            catch (Exception ex)
            {
                updateMsg($"无法读入文件{dxfFile}，{ex.Message}", true);
            }
            if (doc == null)
            {
                updateMsg($"无法读入文件{dxfFile}", true);
                return null;
            }
            else
            {
                var allBlocks = doc.Blocks.ToArray();
                for (int i = allBlocks.Length - 1; i >= 0; i--)
                {
                    Block block = allBlocks[i];
                    if (block.Name.StartsWith("*U", StringComparison.InvariantCultureIgnoreCase) || block.Name.StartsWith("*T", StringComparison.InvariantCultureIgnoreCase))
                        block.Name = hash + block.Name.Replace("*", "");
                }
                return doc;
            }
        }
    }
}
